package com.ezops.poc.util;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.AnnotationsWriter;

import com.ezops.poc.util.Report.Row;

/**
 * generates the ezops model from the file "MasterTemplates"
 * 
 * @author vgaurav
 * 
 */
public class EzopsModelGenerator {

	public static void main(String[] args) throws Exception {
		try {
			Report report = new Report(new File(
					"/Users/vgaurav/EZOPS/MasterTemplate.xlsx"));

			Map<String, String> props = new HashMap<String, String>();

			Iterator<Row> rows = report.iterator();
			while (rows.hasNext()) {
				Row currentRow = rows.next();
				String fieldName = currentRow.getValue(0);

				String pojoField = Character.toLowerCase(fieldName.charAt(0))
						+ fieldName.substring(1, fieldName.length());
				pojoField = pojoField.replace(".", "").replace(" ", "");

				props.put(pojoField, fieldName);

			}

			CtClass generatedClass = EzopsModelGenerator.generate(
					"com.ezops.poc.bo.EzopsOTModel", props);

			ClassFile cf = generatedClass.getClassFile();

			FileOutputStream os = new FileOutputStream(
					"/Users/vgaurav/EZOPS/EzopsOTModel.class");
			DataOutputStream ds = new DataOutputStream(os);
			cf.write(ds);
			os.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public static CtClass generate(String className,
			Map<String, String> properties) throws NotFoundException,
			CannotCompileException, IOException {

		ClassPool pool = ClassPool.getDefault();
		CtClass cc = pool.makeClass(className);

		// add this to define a super class to extend
		// cc.setSuperclass(resolveCtClass(MySuperClass.class));

		// add this to define an interface to implement
		cc.addInterface(resolveCtClass(Serializable.class));

		for (Entry<String, String> entry : properties.entrySet()) {

			CtField ctField = new CtField(resolveCtClass(String.class),
					entry.getKey(), cc);
			ctField.setModifiers(Modifier.PROTECTED);
			cc.addField(ctField);

			ConstPool constpool = cc.getClassFile().getConstPool();
			// create the annotation

			ByteArrayOutputStream output = new ByteArrayOutputStream();
			AnnotationsWriter writer = new AnnotationsWriter(output, constpool);

			writer.numAnnotations(1);
			writer.annotation("com.ezops.poc.annotation.EzopsDisplayName", 1);
			writer.memberValuePair("name");
			writer.constValueIndex(entry.getValue());

			writer.close();
			byte[] attribute_info = output.toByteArray();
			AnnotationsAttribute attr = new AnnotationsAttribute(constpool,
					AnnotationsAttribute.visibleTag, attribute_info);
			
			
			ctField.getFieldInfo().addAttribute(attr);

			// add getter
			cc.addMethod(generateGetter(cc, entry.getKey(), String.class));

			// add setter
			cc.addMethod(generateSetter(cc, entry.getKey(), String.class));
		}

		return cc;
	}

	private static CtMethod generateGetter(CtClass declaringClass,
			String fieldName, Class fieldClass) throws CannotCompileException {

		String getterName = "get" + fieldName.substring(0, 1).toUpperCase()
				+ fieldName.substring(1);

		StringBuffer sb = new StringBuffer();
		sb.append("public ").append(fieldClass.getName()).append(" ")
				.append(getterName).append("(){").append("return this.")
				.append(fieldName).append(";").append("}");
		return CtMethod.make(sb.toString(), declaringClass);
	}

	private static CtMethod generateSetter(CtClass declaringClass,
			String fieldName, Class fieldClass) throws CannotCompileException {

		String setterName = "set" + fieldName.substring(0, 1).toUpperCase()
				+ fieldName.substring(1);

		StringBuffer sb = new StringBuffer();
		sb.append("public void ").append(setterName).append("(")
				.append(fieldClass.getName()).append(" ").append(fieldName)
				.append(")").append("{").append("this.").append(fieldName)
				.append("=").append(fieldName).append(";").append("}");
		return CtMethod.make(sb.toString(), declaringClass);
	}

	private static CtClass resolveCtClass(Class clazz) throws NotFoundException {
		ClassPool pool = ClassPool.getDefault();
		return pool.get(clazz.getName());
	}

}
